/*
 * Copyright 2011 The Rusted Roof, LLC.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.proteusframework.core.runtime;

import org.proteusframework.core.api.IFactoryRegistrar;
import org.proteusframework.core.api.IPluginRegistrar;
import org.proteusframework.core.api.model.IDisplayableName;
import org.proteusframework.core.api.model.INamespace;
import org.proteusframework.core.base.AbstractObject;
import org.proteusframework.core.base.DisplayableName;
import org.proteusframework.core.base.NoSuchNamespaceException;
import org.proteusframework.core.spi.IProteusRegistrarFactory;
import org.proteusframework.core.spi.ProteusRegistrarFactory;
import org.proteusframework.core.util.Assert;

import java.util.*;
import java.util.logging.Logger;

/**
 * Primordial runtime registrar that authoritatively manages all {@link ProteusRegistrarFactory} instances.
 * <p/>
 * Proteus was expressly designed using a triple-bonded factory. This novel three-tier architecture provides a master
 * registrar, this class, that manages one or more plugin categories. Each plugin category maintains its own plugin
 * registrar that manages one or more instances of a given plugin type.
 * <p/>
 * The <code>ProteusRegistrar</code> is the entry point for the triple-bonded factory.
 *
 * @author Tacoma Four
 */
final class ProteusRegistrar extends AbstractObject implements IFactoryRegistrar
{
    /**
     * Logger.
     */
    private static final Logger logger = Logger.getLogger(ProteusRegistrar.class.getCanonicalName());

    /**
     * Registrar map.
     */
    private final Map<Integer, IProteusRegistrarFactory> registrar
            = new HashMap<Integer, IProteusRegistrarFactory>();

    /**
     * Fully qualified name for the <code>ProteusRegistrar</code>.
     */
    public static final IDisplayableName DN = new DisplayableName("org.proteusframework.runtime",
            "ProteusRegistrar",
            "Factory Registrar",
            "Primordial factory that is responsible for creating other factories");

    /**
     * Constructs a Proteus Registrar that leverages and publishes the services provided by the platform delegate
     * instance provided.
     */
    ProteusRegistrar()
    {
        super(DN);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void onDelegateInjected()
    {
        //
        // Locate and load all of the ProteusRegistrarFactory instances declared in the CLASSPATH

        logger.info("ProteusRegistrar onDelegateInjected()");

        ServiceLoader<ProteusRegistrarFactory> loader = ServiceLoader.load(ProteusRegistrarFactory.class);

        for (ProteusRegistrarFactory registrarFactory : loader)
        {
            String displayName = registrarFactory.getDisplayableName().toString();
            logger.info("Registering factory " + displayName + " with " + ProteusRegistrar.class.getName() + " (" + registrarFactory.getDisplayableName().hashCode() + ")");

            registrar.put(registrarFactory.getDisplayableName().hashCode(), registrarFactory);
        }
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public void addRegistrar(IProteusRegistrarFactory registrarFactory)
    {
        registrar.put(registrarFactory.getDisplayableName().hashCode(), registrarFactory);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<IProteusRegistrarFactory> listFactories()
    {
        Collection<IProteusRegistrarFactory> safeCopy = new ArrayList<IProteusRegistrarFactory>();
        safeCopy.addAll(registrar.values());

        return safeCopy;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public IPluginRegistrar createRegistrar(INamespace namespace) throws NoSuchNamespaceException
    {
        IPluginRegistrar pluginRegistrar = null;

        Assert.parameterNotNull(namespace, "Parameter 'namespace' must not be null");

        if (isInstalled(namespace))
        {
            IProteusRegistrarFactory registrarProvider = registrar.get(namespace.hashCode());
            pluginRegistrar = registrarProvider.createFactory(delegate, delegateInterface);
        } else
        {
            logger.warning("Proteus Factory Registrar has no record of a " + namespace.toString());
            throw new NoSuchNamespaceException(namespace);
        }

        return pluginRegistrar;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isInstalled(INamespace namespace)
    {
        IProteusRegistrarFactory factory = registrar.get(namespace.hashCode());
        return (null != factory);
    }
}
